Spring通过org.springframework.aop.TargetSource接口提供了TargetSource的概念。这个接口负责返回实现了Joint point的目标对象。TargetSource在每次AOP代理处理方法调用时,都会被要求产生一个目标对象。
使用Spring AOP的开发者通常不需要直接使用TargetSources,但这为池,热交换和其他复杂的目标提供了强大的支持。比如,一个池化的TargetSource可以为每个调用返回不同的目标对象,并用池管理这些实例。
如果你没有特指TargetSource,会使用包装本地对象的默认实现。(正如你所期望的)每次调用返回的都是相同的目标。让我们看一下Spring提供的标准的target Source和你该如何使用它们。

当使用自定义的target source时,你的目标通常要是原型的,而非单例的。这允许Spring在需要时创建新的实例。

12.10.1 Hot swappable target sources

org.springframework.aop.target.HotSwappleTargetSource的存在允许AOP 代理的目标在调用者仍持有它们引用的情况下替换掉它们。
改变target source的目标会被立刻生效。HotswappableTargetSource是线程安全的。
你可以通过HotSwappableTargetSource的swap()方法来改变目标:

HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper");
Object oldTarget = swapper.swap(newTarget);

XML定义如下:

<bean id="initialTarget" class="mycompany.OldTarget"/>

<bean id="swapper" class="org.springframework.aop.target.HotSwappableTargetSource">
    <constructor-arg ref="initialTarget"/>
</bean>

<bean id="swappable" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="targetSource" ref="swapper"/>
</bean>

调用上面的swap()会改变swappable bean的目标。持有这个引用的客户端并不会知道这个变化的过程,但是变化立即生效了。
尽管这个例子中没有添加任何的通知——并且在使用TargetSource时不需要添加通知——当然任何TargetSource可以和任意通知结合使用。

12.10.2 Pooling target sources

池化的target source提供了和无状态会话EJB相似的编程模型,池中维护着不同的实例,当方法被调用时,可以从池中获取对象。
Spring池和SLSB池关键的区别是Spring池可以应用于任何POJO。通常对Spring而言,服务可以以非入侵的方式被应用。
Spring提供了对Commons Pool 2.2开箱即用的支持,它提供了相当有效率池化实现。如果要使用这个特性,你需要将commons-pool的Jar包添加到应用程序的类路径下。也可以继承org.springframework.aop.target.AbstractPoolingTargetSource来支持其他的池化API。

Spring框架4.2起也支持Commons Pool 1.5+。

下面是配置实例:

<bean id="businessObjectTarget" class="com.mycompany.MyBusinessObject"
        scope="prototype">
    ... properties omitted
</bean>

<bean id="poolTargetSource" class="org.springframework.aop.target.CommonsPool2TargetSource">
    <property name="targetBeanName" value="businessObjectTarget"/>
    <property name="maxSize" value="25"/>
</bean>

<bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="targetSource" ref="poolTargetSource"/>
    <property name="interceptorNames" value="myInterceptor"/>
</bean>

注意,目标对象——这个例子中是"businessObjectTarget"——必须要是原型的。它允许PoolingTargetSource的实现在需要的时候让池创建一个新的实例。查看Java文档了解AbstractPoolingTargetSource和你所希望使用的具体子类的属性:最基础的属性时"maxSize",且必须要保证存在这个属性。
在这个例子中,"myInterceptor"是定义在同一个IoC容器中的拦截器。然而,在使用池时不需要指定拦截器。如果你只希望池化且没有其他的通知,那就不需要设置interceptorsNames的属性。
可以通过Spring配置让被池管理的对象能够转成org.springframework.aop.target.PoolingConfig接口,它可以通过引入将池的当前大小和配置信息传递出来。你需要添加一个像这样的advisor:

<bean id="poolConfigAdvisor" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetObject" ref="poolTargetSource"/>
    <property name="targetMethod" value="getPoolingConfigMixin"/>
</bean>

因为我们使用了MethodInvokingFactoryBean,advisor会在AbstractPoolingTargetSource类调用方法时被获得。这个advisor的名字(这里时"poolingConfigAdvisor")一定要在ProxyFactoryBean的拦截器名字的列表中,以暴露被池化的对象。
可以像下面这样转型:

PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject");
System.out.println("Max pool size is " + conf.getMaxSize());

通查,池化管理无状态的服务对象是不必要的。我们不认为这要作为默认的选择,因为大多数无状态的对象时线程安全的,并且如果资源被缓存的话是有问题的。

更简单的池可以使用自动代理。可以为自动代理创造器设置TargetSources。

12.10.3 Prototype target sources

设置“原型”的target source和池化TargetSource很像。这种情况下,秒内次调用方法时,都会为目标创建新的实例。尽管在现代的JVM中,创建新对象的话费并不是很高,但(满足IoC依赖的)织入的代价可能会很高。因此在没有特别好的理由下,你不应该使用这个方法。
为了实现这点,你需要将上面的poolTargetSource定义做些修改(为了更清晰,我还改了名字。)

<bean id="prototypeTargetSource" class="org.springframework.aop.target.PrototypeTargetSource">
    <property name="targetBeanName" ref="businessObjectTarget"/>
</bean>

这里只有一个属性:目标bean的名字。它继承而来,保证了在TargetSource的各实现中命名的一致性。和pooling target source一样,(prototype target source的)目标bean必须要是原型的bean定义。

12.10.4 ThreadLocal taget sources

ThreadLocal target sources在你需要为每个请求(即每个线程)创建一个对象时十分有用。ThreadLocal的概念是在JDK级别为每个线程透明的存储资源。设置TheadLocalTargetSource和其他说明过的target source类型很相似:

<bean id="threadlocalTargetSource" class="org.springframework.aop.target.ThreadLocalTargetSource">
    <property name="targetBeanName" value="businessObjectTarget"/>
</bean>

在多线程或时多个类加载器环境中,不正确的使用ThreadLocals可能会导致严重的问题(可能引起内存泄漏)。应该考虑用其他类包装threadlocal,而不是直接使用。并且应该记得中正确为线程设置和取消(可以简单的调用ThreadLocal.set(null))资源。无论什么情况下都要记住取消资源,否则可能会引发问题。Spring的ThreadLocal支持为你做了这一点,但你再使用其他没有正确的处理代码的ThreadLocal时,要考虑这点。